feat: add hub funded spoke pool testnet VIP - #762
Conversation
- Foundation draft only. The spoke comptroller, markets, adapter and yield source are unbuilt, so their addresses are zero placeholders and the proposal cannot be simulated until they land. - Command order is load-bearing in four places: acceptOwnership before any owner-gated setter, setPriceOracle before addPool, the bounded oracle before the pool serves a borrow or redeem, and addMarket before the supply allowlist is armed. - Grants only the six ACM roles this chain does not already cover. The rest are held by the timelock or the registry as wildcards, checked on chain - Risk parameters the PRD does not specify are left as TODO, as are the bStock liquidation calls, which have no testnet target yet.
…ase 1 - Refined comments and descriptions in config.ts for clarity and accuracy regarding pool mechanics and risk factors. - Adjusted liquidation incentive and collateral factor definitions to align with updated PRD references. - Enhanced the permissions.ts file to include necessary role strings for the new Spoke pool, ensuring proper access control. - Added comments to clarify the implications of wildcard grants and the necessity of specific role grants for the new registry. - Introduced new roles for governance and registry-driven actions, ensuring smooth operation of the Spoke pool.
Greptile SummaryThis PR introduces the BNB Chain testnet Phase 1 VIP for a Hub-Funded Spoke pool.
Confidence Score: 4/5The PR is not safe to use or merge as an executable VIP until proposal-consumed zero addresses are replaced; the unresolved risk configuration and missing simulation should also be addressed. The proposal deterministically attempts to configure zero-address spoke contracts and upgrade ProtocolShareReserve to a zero implementation, so governance execution cannot complete in its current form. The remaining findings concern risk sign-off, unused IRM configuration, and absent regression coverage. Files Needing Attention: vips/vip-671/addresses/bsctestnet.ts, vips/vip-671/bsctestnet.ts, vips/vip-671/config.ts
|
| Filename | Overview |
|---|---|
| vips/vip-671/addresses/bsctestnet.ts | Adds the testnet address book, but proposal-critical spoke and implementation addresses remain zero placeholders. |
| vips/vip-671/bsctestnet.ts | Builds the ordered governance payload, but lacks fork simulation and does not apply or verify configured market IRMs. |
| vips/vip-671/config.ts | Defines four markets and reserve distribution constants, with several materially consequential risk values still awaiting approval. |
| vips/vip-671/permissions.ts | Defines registry and governance ACM roles using a command-builder pattern consistent with existing VIPs. |
Reviews (1): Last reviewed commit: "feat(: update Hub-Funded Spoke pool para..." | Re-trigger Greptile
| export const PROTOCOL_SHARE_RESERVE_IMPL = "0x0000000000000000000000000000000000000000"; | ||
|
|
||
| /// TODO(deploy): isolated-pools `deploy/024-deploy-spoke-pool-registry.ts` -> `SpokePoolRegistry`. | ||
| /// A SECOND `PoolRegistry` instance, behind the chain's existing DefaultProxyAdmin. The spoke pool is | ||
| /// deliberately kept out of the isolated-pools directory that the indexer, the frontend pool list and | ||
| /// the risk tooling all iterate. | ||
| /// `Ownable2Step`: the deploy script only NOMINATES the Normal Timelock, so this VIP must call | ||
| /// `acceptOwnership()` before `addPool`. | ||
| export const SPOKE_POOL_REGISTRY = "0x0000000000000000000000000000000000000000"; |
There was a problem hiding this comment.
Proposal targets zero addresses
The executable proposal still uses zero-address placeholders for the spoke registry, comptroller, markets, and new reserve implementation. If vip671 is submitted in this state, its calls cannot configure the spoke contracts, and the ProxyAdmin upgrade to the zero implementation will revert, preventing proposal execution. Replace every address consumed by the proposal before merging the executable VIP.
| collateralFactor: parseUnits("0.75", 18), // PRD §6 | ||
| liquidationThreshold: parseUnits("0.8", 18), // PRD §6 | ||
| supplyCap: parseUnits("236", 18), // TODO(risk): copied from VIP-633's Core listing. | ||
| }; | ||
|
|
||
| export const MARKET_NVDAB: SpokeMarket = { | ||
| ...collateralDefaults, | ||
| symbol: "vNVDAB_HubSpoke", | ||
| vToken: VNVDAB_SPOKE, | ||
| underlying: NVDAB, | ||
| collateralFactor: parseUnits("0.75", 18), // PRD §6 | ||
| liquidationThreshold: parseUnits("0.8", 18), // PRD §6 | ||
| supplyCap: parseUnits("450", 18), // TODO(risk): copied from VIP-633's Core listing. | ||
| }; | ||
|
|
||
| export const MARKET_SPCXB: SpokeMarket = { | ||
| ...collateralDefaults, | ||
| symbol: "vSPCXB_HubSpoke", | ||
| vToken: VSPCXB_SPOKE, | ||
| underlying: SPCXB, | ||
| collateralFactor: parseUnits("0.7", 18), // PRD §6 | ||
| liquidationThreshold: parseUnits("0.75", 18), // PRD §6 |
There was a problem hiding this comment.
These collateral factors and liquidation thresholds are explicitly unresolved risk placeholders, but proposal tooling does not prevent them from entering the governance payload. Compared with VIP-633 for the same assets, these factors allow 25% more borrowing for TSLAB and NVDAB and 40% more for SPCXB. Obtain risk approval and replace or explicitly validate these values before making the proposal executable.
- give the guardian the same six spoke comptroller roles as the normal timelock. testnet only, so testing can add a supplier, lift an allowlist or force a liquidation without a proposal each time - move the "already granted on this chain" list out of comments into ASSUMED_WILDCARD_ROLES and assert it before the proposal runs, so a revoked wildcard fails the simulation instead of the execution - assert the ownership the reserve upgrade and addPoolRegistry rest on
What this VIP does
Phase 1 of the Hub-Funded Spoke pool on BNB Chain testnet. Covers the spoke pool side only (isolated-pools#559). Liquidity Hub wiring (venus-liquidity-hub#22) and bStock liquidation (venus-protocol#707) are Phase 2.
Executed on bsctestnet on 2026-09-09 as GovernorBravo proposal 715.
44 commands. Every address is deployed and was read back from bsctestnet. Covered end to end by
simulations/vip-671/bsctestnet.ts, which asserts the pre-VIP state, the permissions the VIP assumes rather than grants, every command, and the post-VIP state.Permissions
Everything else is already covered by wildcard grants on this chain, verified on chain rather than assumed.
Two things worth calling out. The registry grants are the easy miss: the identical wildcards exist but name the isolated-pools registry as the account, so the spoke registry inherits none and addPool reverts without them. And the Guardian grants are a bsctestnet decision, so QA can arm and lift either allowlist and flip forced liquidation without a proposal per test case. A mainnet listing should grant these to the Normal Timelock alone. The pause path already reaches all three timelocks through existing wildcards, which is why the emergency timelocks receive nothing here.
Notes
helpers/spokeDeploymentConfig.ts, the file the deployment was run from, so the listing matches what was deployed. They mirror the isolated Stablecoins pool on this network, because a spoke pool restricts who may supply, borrow and liquidate rather than taking more risk per market.setAllowedSupplier.Known follow-up, not blocking
RiskFundConverter still resolves through a single registry. It stops being a PSR destination after step 5, so it does not affect this launch, but it needs the same multi-registry treatment before any spoke pool reaches mainnet. Noted on protocol-reserve#168.